require 'rake'
require 'digest/md5'

task :xlr8 => ['xlr8:copy_to_s3', 'xlr8:edge_cache', 'xlr8:finalize_config']

namespace :xlr8 do
  desc <<-DESC
    xlr8 your rails application. This should not be invoked locally, but by a capistrano
    task that knows about your release version
  DESC
  task :default do
  end
  
  desc <<-DESC
  DESC
  task :load_configuration => :environment do
    config_path = File.join( RAILS_ROOT, 'config', 'xlr8.yml' )
    fail "File #{config_path} not found!" unless File.exist?( config_path )
    @xlr8_config = HashWithIndifferentAccess.new( YAML::load_file( config_path ) )
    if( @xlr8_config.has_key?('aws_proxy') )
      proxy_name = @xlr8_config['aws_proxy']
      begin
        # trying to require override proxy
        @xlr8_aws_proxy = proxy_name.constantize
      rescue
        fail "couldn't load proxy #{proxy_name}: #{$!}"
      end
    end
    @xlr8_bucket = ENV['application'] unless( ENV['application'].blank? )
    @xlr8_bucket = @xlr8_config['bucket']
    fail "couldn't figure out bucket name from config" if @xlr8_bucket.blank?
    if( @xlr8_aws_proxy.blank? )
      require 'xlr8/proxy'
      @xlr8_aws_proxy = Xlr8::Proxy.new( @xlr8_config['aws_access_key_id'], @xlr8_config['aws_secret_access_key'], @xlr8_bucket )
    end
    @xlr8_release = ENV['RELEASE_NAME'] || Time.now.utc.strftime("%Y%m%d%H%M%S") # consider warning the user when we default to Time.now.to_i
  end

  task :mime_types do
    @mime_types = YAML::load_file( File.join( File.dirname(__FILE__), '..', '..', '..', 'config', 'mime_types.yml' ) )
    @ext_to_mime = Hash[ *( @mime_types.collect{ |k,vv| vv.collect{ |v| [v,k] } }.flatten ) ]
  end

  desc <<-DESC
  Prepare to xlr8. This is mainly here so that people can hook into it
  and perform tasks such as minification, glomming together multiple js or css
  files into one, etc.

  Also see the capistrano task xlr8:prepare
  DESC
  task :prepare => :load_configuration do
  end

  desc <<-DESC
  DESC
  task :copy_to_s3 => [:prepare, :mime_types] do
    puts "computing md5-sums, and uploading all files to s3. This may take a while."
    permissions = 'public-read'
    files = FileList.new( "#{RAILS_ROOT}/public/**/*" ) # what's the right way to do this?
    names = Hash[ *(files.collect{ |f| [f, f.gsub( /^#{RAILS_ROOT}\/public/, @xlr8_release ) ] }.flatten) ]
    names.each do |file, name|
      next if File.directory?( file )
      ext = File.extname( file )[1..-1]
      mime = @ext_to_mime[ext] || 'application/octet-stream'
      data = File.open( file ).read
      md5 = Digest::MD5.new.digest( data )
      object_http_headers = {
        'Content-Type' => mime,
        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9
        'Cache-Control' => "max-age=#{10.years.to_i}",
        # http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.21
        # "HTTP/1.1 servers SHOULD NOT send Expires dates more than one year in the future."
        'Expires' => ( Time.now + 1.year ).utc.to_s,
        # FIXME: why isn't our md5 working as expected?
        'Content-MD5' => Base64.encode64(md5).strip
      }
      meta_headers = {}
      to_s3_params = [name, data, meta_headers, permissions, object_http_headers]
      # puts "putting to s3 with params: #{name}, #{file}, #{meta_headers.inspect}, #{permissions}, #{object_http_headers.inspect}"
      @xlr8_aws_proxy.to_s3( *to_s3_params )
    end # each name

    @xlr8_s3_location = "#{@xlr8_bucket}.s3.amazonaws.com/#{@xlr8_release}"
    @xlr8_location = @xlr8_s3_location
  end

  task :load_origin_information do
    # TODO: allow bucket and release to be passed in as env variables
    if( @xlr8_bucket.blank? )
      Rake::Task['xlr8:prepare'].invoke
    end
  end

  desc <<-DESC
  DESC
  task :edge_cache => :load_origin_information do
    info = @xlr8_aws_proxy.edge_cache( "#{@xlr8_bucket}.s3.amazonaws.com" )
    if( info[:active] )
      @xlr8_edge_cache_location = "#{info[:domain_name]}/#{@xlr8_release}"
      @xlr8_location = @xlr8_edge_cache_location
      Rake::Task['xlr8:finalize_config'].invoke
    else
      puts <<-EOM
      ****

      Edge caching is not (yet?) active. Cloudfront take 1-2 hours to create a distribution.

      You can try calling rake xlr8:edge_cache again after a while.

      ****
        Details: #{info[:details].inspect}
      ****
        EOM
    end
  end

  task :compute_xlr8_location do
    # allow the location to be passed in as an env variable.
    raise "xlr8 location wasn't set! call rake xlr8" if( @xlr8_location.blank? )
  end

  desc <<-DESC
  DESC
  task :finalize_config => :compute_xlr8_location do
    config_file = File.open( File.join( RAILS_ROOT , 'config', 'environment.rb' ), "a+" )
    config_file << <<-EOT
    # <xlr8>
    #   at #{Time.now.utc.to_s}, 
    #   overriding environment.rb to include new asset host:

    ActionController::Base.asset_host = "#{@xlr8_location}"

    # </xlr8>
    EOT
    config_file.close
    puts "Your environment.rb file now contains a pointer to the new xlr8 location: #{@xlr8_location}"
  end

end
